package com.zl.zsk.service.impl;

import com.google.common.collect.Lists;
import com.zl.zsk.config.security.JwtProvider;
import com.zl.zsk.constant.RedisKeys;
import com.zl.zsk.constant.RetCode;
import com.zl.zsk.exception.AuthDenyException;
import com.zl.zsk.exception.BizException;
import com.zl.zsk.exception.NotFoundException;
import com.zl.zsk.pojo.SysMenu;
import com.zl.zsk.pojo.SysUser;
import com.zl.zsk.pojo.vo.PageInfo;
import com.zl.zsk.pojo.vo.UserVo;
import com.zl.zsk.repository.SysMenuRepository;
import com.zl.zsk.repository.SysUserRepository;
import com.zl.zsk.service.UserService;
import com.zl.zsk.util.FileUtil;
import com.zl.zsk.util.IdWorker;
import com.zl.zsk.util.MenuUtil;
import com.zl.zsk.util.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * @author lxb
 * @version 1.0.0
 * @desc:
 * @date 2020-03-13 10:56
 */
@Service("userService")
public class UserServiceImpl implements UserService {
    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    @Autowired
    private SysUserRepository sysUserRepository;

    @Autowired
    private SysMenuRepository sysMenuRepository;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtProvider jwtProvider;

    @Autowired
    private RedisUtil redisUtil;

    @Value("${worker.id}")
    private long workerId;

    @Value("${worker.centerid}")
    private long centerId;

    @Value("${worker.sequence}")
    private long sequence;

    @Override
    public UserDetails loadUserByUsername(String loginName) {
        log.info("--开始通过数据库查询用户--");
        SysUser sysUser = sysUserRepository.findSysUserByLoginName(loginName).orElseThrow(() -> new UsernameNotFoundException("用户名不存在: " + loginName));

        List<SysMenu> menus = sysMenuRepository.findSysMenusByLoginName(loginName);

//        List<SimpleGrantedAuthority> authorities = menus.stream().map(menu -> new SimpleGrantedAuthority(menu.getMenuTag())).collect(Collectors.toList());
        //设值授权
        List<SysMenu> sysMenus = MenuUtil.list2Tree(menus);
        sysUser.setMenus(sysMenus);
        return sysUser;
    }

    @Override
    public String authLogin(String userName, String password) {
        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userName,password);
        Authentication authenticate = authenticationManager.authenticate(authToken);
        SysUser principal =(SysUser) authenticate.getPrincipal();
        SecurityContextHolder.getContext().setAuthentication(authenticate);
        String token = jwtProvider.generateJwtToken(authenticate);
        String redisKey = String.format(RedisKeys.USER_TOKEN,token);
//        UserVo userVo = UserVo.getUserWithSysUser(principal);
        //将用户token信息保存进redis
        redisUtil.set(redisKey,principal,RedisKeys.HALF_HOUR);
        return token;
    }

    @Override
    public SysUser getUserFromToken(String token) {
        boolean b = jwtProvider.validateJwtToken(token);
        if(!b){
            throw new AuthDenyException(RetCode.AUTH_DENY);
        }
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        SysUser sysUser = (SysUser) authentication.getPrincipal();
        return sysUser;
    }


    @Override
    public void logout(String token){
        SecurityContextHolder.clearContext();
        redisUtil.delete(String.format(RedisKeys.USER_TOKEN,token));
    }

    @Override
    public List<SysMenu> getUserMenus() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        SysUser sysUser = (SysUser) authentication.getPrincipal();
        List<SysMenu> menus = sysUser.getMenus();
        return menus;
    }

    /**
     * 添加用户
     * @param sysUser
     */
    @Override
    @PreAuthorize("hasAuthority('user:manage')")
    public void addUser(SysUser sysUser) {
        //当前用户
        SysUser nowUser =(SysUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String password = sysUser.getPassword();

        Long deptId = sysUser.getDeptId();
        //如果新增的时候没有选择部门，则与创建人同一部门或者公司
        if(deptId == null||deptId.longValue()==0){
            sysUser.setDeptId(nowUser.getDeptId());
        }
        if(StringUtils.isEmpty(password)){
            //如果没填密码则默认密码为6个0
            password = "000000";
        }
        sysUser.setCompanyId(nowUser.getCompanyId());
        sysUser.setPassword(passwordEncoder.encode(password));
        sysUser.setCreateBy(nowUser.getUserId());
        sysUser.setCreateTime(new Date());
        sysUser.setNonDel(true);
        sysUserRepository.save(sysUser);
    }


    //TODO:后期通过反射统一封装多条件查询
    /**
     * 查询用户列表
     *
     * @param userVo
     * @return
     */
    @Override
    @PreAuthorize("hasAuthority('user:manage')")
    public PageInfo<SysUser> listUser(UserVo userVo) {
        int page = userVo.getPage();
        int size = userVo.getSise();
        Pageable pageable = PageRequest.of(page-1, size);
        Page<SysUser> users = sysUserRepository.findAll((root,criteriaQuery,criteriaBuilder)->{
            List<Predicate> list = new ArrayList<>();
            //部门id
            Long deptId = userVo.getDeptId();
            //登录账号
            String loginName = userVo.getLoginName();
            //手机号
            String mobile = userVo.getMobile();
            if(deptId !=null){
                list.add(criteriaBuilder.equal(root.get("deptId"),deptId));
            }
            if(!StringUtils.isEmpty(loginName)){
                list.add(criteriaBuilder.equal(root.get("loginName"),loginName));
            }

            if(!StringUtils.isEmpty(mobile)){
                list.add(criteriaBuilder.like(root.get("mobile"),mobile));
            }
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        },pageable);

        return PageInfo.buildPageInfo(users,page,size);
    }

    /**
     * 编辑用户
     * 公司老板对公司有所有权限
     * @param sysUser
     */
    @Override
    @PreAuthorize("hasAuthority('user:manage')")
    public void editUser(SysUser sysUser) {
        Optional<SysUser> optional = sysUserRepository.findById(sysUser.getUserId());
        if(!optional.isPresent()){
            throw new NotFoundException(RetCode.NULLDATA);
        }
        //TODO:未完待续
    }

    /**
     * 上传用户头像
     * @param file
     * @return 返回新的文件名
     */
    @Override
    public String uploadAvatar(MultipartFile file) {
        List<String> imageType = Lists.newArrayList(".jpg",".jpeg",".png");
        //文件名
        String fileName = file.getOriginalFilename();
        //获取文件后缀
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        String newName = fileName+new IdWorker(workerId,centerId,sequence).nextId();
        if(!imageType.contains(suffixName)){
            throw new BizException(RetCode.FILE_ILLEGAL);
        }

        String filePath = FileUtil.getFilePath();
        String path = filePath+newName;
        File destFile = new File(path);
        if(!destFile.getParentFile().exists()){
            destFile.getParentFile().mkdirs();
        }
        try {
            file.transferTo(destFile);
        } catch (IOException e) {
            e.printStackTrace();
            throw new BizException(RetCode.FILE_UPLOAD_FAIL);
        }
        return newName;
    }

    @Override
    public byte[] getAvatar(String name) {
        String filePath = FileUtil.getFilePath();
        File file = new File(filePath+name);
        byte[] bytes;
        try {
            bytes = FileUtil.fileToByte(file);
        } catch (IOException e) {
            throw new BizException(RetCode.File_READ_FAIL);
        }
        return bytes;
    }

    /**
     * 修改密码
     * @param
     */
    @Override
    public void editPassword(String oldPassword,String newPassword) {
        SysUser sysUser = (SysUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String realPassword = sysUser.getPassword();
        boolean matches = passwordEncoder.matches(oldPassword, realPassword);
        if(matches){
            sysUser.setPassword(passwordEncoder.encode(newPassword));
            sysUserRepository.save(sysUser);
        }else{
            throw new BizException(RetCode.PASSWORD_ERROR);
        }
    }

}
